<?php
/**
 * Celebrio Library
 *
 * @copyright  Copyright (c) 2011 Celebrio Software
 * @package    GoogleAPI
 */
namespace Celebrio\Mail;

use Nette\Object;
use \WebtopModule\SystemAppModule\CelebrioMailModule\Attachment;
use Nette\Image;

class MailReceiver extends Object {
    private $server='';
    private $username='';
    private $password='';
    private $imap_stream='';
    private $email='';
    private $imageArray = array("IMAGE/JPEG", "IMAGE/PNG", "IMAGE/GIF");

    public function __construct($username,$password,$EmailAddress,$mailserver='localhost',$servertype='pop',$port='110',$ssl = false)
    {
        if($servertype=='imap')
        {
            if($port=='') $port='143';
            $strConnect='{'.$mailserver.':'.$port.($ssl ? "/ssl" : "").'}';
        } else {
            $strConnect='{'.$mailserver.':'.$port. '/pop3'.($ssl ? "/ssl" : "").'}';
        }
        $this->server = $strConnect;
        $this->username = $username;
        $this->password = $password;
        $this->email = $EmailAddress;
    }

    /**
     * connects to the server
     * @return boolean false if connection fails, true if it works
     */
    public function connect($folder="INBOX")
    {
        $this->imap_stream=@imap_open($this->server.$folder,$this->username,$this->password,OP_SILENT);
	// next two lines silence imap errors
	imap_errors();
	imap_alerts();
        if($this->imap_stream) return true;
        else return false;
    }

    /**
     * gets mail folders for sent, drafts, trash and spam
     * usually supported only by IMAP
     * @return array("Sent"=>string,"Drafts"=>string,"Trash"=>string,"Spam"=>string)
     */
    public function getMailFolders() {
	$folders = array("Sent"=>"","Drafts"=>"","Trash"=>"","Spam"=>"");
	$list = \imap_list($this->imap_stream, $this->server, "*");
	if(\is_array($list)){
	    $offset = \strlen($this->server)-1;
	    foreach($list as $row){
		if(stripos($row,"Sent",$offset))$folders["Sent"] = \substr($row,\strpos($row,"}",$offset-1)+1,\strlen($row));
		elseif(stripos($row,"Draft",$offset))$folders["Drafts"] = \substr($row,\strpos($row,"}",$offset-1)+1,\strlen($row));
		elseif(stripos($row,"Trash",$offset)||stripos($row,"Deleted",$offset))$folders["Trash"] = \substr($row,\strpos($row,"}",$offset-1)+1,\strlen($row));
		elseif(stripos($row,"Spam",$offset)||stripos($row,"Junk",$offset))$folders["Spam"] = \substr($row,\strpos($row,"}",$offset-1)+1,\strlen($row));
	    }
	}
	return $folders;
    }

    /**
     * Receive a string with a mail header and returns it decoded to a specified charset.
     * If the charset specified into a piece of text from header isn't supported by "mb", the "fallbackCharset" will be used to try to decode it.
     * @param <type> $mimeStr
     * @param <type> $inputCharset
     * @param <type> $targetCharset
     * @param <type> $fallbackCharset
     * @return <type>
     */
    public function decodeMimeString($mimeStr, $inputCharset='utf-8', $targetCharset='utf-8', $fallbackCharset='iso-8859-2') {
	$encodings=mb_list_encodings();
	for ($n=sizeOf($encodings); $n--; ) { $encodings[$n]=strtolower($encodings[$n]); }
	$inputCharset=strtolower($inputCharset);
	$targetCharset=strtolower($targetCharset);
	$fallbackCharset=strtolower($fallbackCharset);
        
	$decodedStr='';
	$mimeStrs=imap_mime_header_decode($mimeStr);
	for ($n=sizeOf($mimeStrs), $i=0; $i<$n; $i++) {
	    $mimeStr=$mimeStrs[$i];
	    $mimeStr->charset=strtolower($mimeStr->charset);
	    if (($mimeStr == 'default' && $inputCharset == $targetCharset) //WTF?? nema to byt skor $mimeStr->charset == 'default'
		|| $mimeStr->charset == $targetCharset) {
		$decodedStr.=$mimeStr->text;
	    } else {
                $decodedStr.=mb_convert_encoding(
		$mimeStr->text, $targetCharset,
		(in_array($mimeStr->charset, $encodings) ?
		$mimeStr->charset : $fallbackCharset));                
	    }
	}
	return $decodedStr;
    }

    /**
     * gets an e-mail header
     *
     * toaddress - full to: line, up to 1024 characters
     * to - an array of objects from the To: line, with the following properties: personal, adl, mailbox, and host
     * fromaddress - full from: line, up to 1024 characters
     * from - an array of objects from the From: line, with the following properties: personal, adl, mailbox, and host
     * ccaddress - full cc: line, up to 1024 characters
     * cc - an array of objects from the Cc: line, with the following properties: personal, adl, mailbox, and host
     * bccaddress - full bcc: line, up to 1024 characters
     * bcc - an array of objects from the Bcc: line, with the following properties: personal, adl, mailbox, and host
     * reply_toaddress - full Reply-To: line, up to 1024 characters
     * reply_to - an array of objects from the Reply-To: line, with the following properties: personal, adl, mailbox, and host
     * senderaddress - full sender: line, up to 1024 characters
     * sender - an array of objects from the Sender: line, with the following properties: personal, adl, mailbox, and host
     * return_pathaddress - full Return-Path: line, up to 1024 characters
     * return_path - an array of objects from the Return-Path: line, with the following properties: personal, adl, mailbox, and host
     * remail -
     * date - The message date as found in its headers
     * Date - Same as date
     * subject - The message subject
     * Subject - Same a subject
     * in_reply_to -
     * message_id -
     * newsgroups -
     * followup_to -
     * references -
     * Recent - R if recent and seen, N if recent and not seen, ' ' if not recent.
     * Unseen - U if not seen AND not recent, ' ' if seen OR not seen and recent
     * Flagged - F if flagged, ' ' if not flagged
     * Answered - A if answered, ' ' if unanswered
     * Deleted - D if deleted, ' ' if not deleted
     * Draft - X if draft, ' ' if not draft
     * Msgno - The message number
     * MailDate -
     * Size - The message size
     * udate - mail message date in Unix time
     * fetchfrom - from line formatted to fit fromlength characters
     * fetchsubject - subject line formatted to fit subjectlength characters
     *
     * @param int $mail_number
     * @return header object
     */
    public function getHeader($mail_number) {
	if(!$this->imap_stream) return false;

	return imap_headerinfo($this->imap_stream, $mail_number);
    }
    
    
    public function getHeaders($start, $end) {
        if(!$this->imap_stream) return false;
        $headers = array();
        $sequence = $start.":".$end;
        $headers = imap_fetch_overview($this->imap_stream, $sequence);
        return $headers;
    }

    function get_mime_type(&$structure) //Get Mime type Internal Private Use
    {
        $primary_mime_type = array("TEXT", "MULTIPART", "MESSAGE", "APPLICATION", "AUDIO", "IMAGE", "VIDEO", "OTHER");
        if($structure->subtype) {
            return $primary_mime_type[(int) $structure->type] . '/' . $structure->subtype;
        }
        return "TEXT/PLAIN";
    }

    /**
     * Get Part Of Message
     * @param <type> $stream
     * @param <type> $msg_number
     * @param <type> $mime_type
     * @param <type> $structure
     * @param string $part_number
     * @return <type>
     */
    private function get_part($stream, $msg_number, $mime_type, $structure = false, $part_number = false)
    {
        if(!$structure) {
            $structure = imap_fetchstructure($stream, $msg_number);
        }
        if($structure) {
            if($mime_type == $this->get_mime_type($structure))
            {
                if(!$part_number)
                {
                    $part_number = "1";
                }
                $text = imap_fetchbody($stream, $msg_number, $part_number);
                if($structure->encoding == 3)
                {
                    return imap_base64($text);
                }
                else if($structure->encoding == 4)
                {
                    return imap_qprint($text);
                }
                else
                {
                    return $text;
                }
            }
            if($structure->type == 1) /* multipart */
            {
                if($structure->subtype === "MIXED") {
                }
                while(list($index, $sub_structure) = each($structure->parts))
                {
                    $prefix = null;
                    if($part_number)
                    {
                        $prefix = $part_number . '.';
                    }
                    $data = $this->get_part($stream, $msg_number, $mime_type, $sub_structure, $prefix . ($index + 1));
                    if($data)
                    {
                        return $data;
                    }
                }
            }
        }
        return false;
    }

    /**
     * Get Total Number off Email In Mailbox
     * @return false or a number of unread mails
     */
    public function getTotalMails()
    {
        if(!$this->imap_stream) return false;

        return imap_num_msg($this->imap_stream);
    }

    function GetAttach($mid,$path = null) // Get Atteced File from Mail
    {
        if(!$this->imap_stream)
            return false;

        $struckture = imap_fetchstructure($this->imap_stream,$mid);
        $ar="";
        if($struckture->parts)
        {
            $data = array();
            foreach($struckture->parts as $key => $value)
            {
                $enc=$struckture->parts[$key]->encoding;
                if($struckture->parts[$key]->ifdparameters)
                {
                    $name=$struckture->parts[$key]->dparameters[0]->value;
                    $message = imap_fetchbody($this->imap_stream,$mid,$key+1);
                    if ($enc == 0)
                        $message = imap_8bit($message);
                    if ($enc == 1)
                        $message = imap_8bit ($message);
                    if ($enc == 2)
                        $message = imap_binary ($message);
                    if ($enc == 3)
                        $message = imap_base64 ($message);
                    if ($enc == 4)
                        $message = quoted_printable_decode($message);
                    if ($enc == 5)
                        $message = $message;
//                    $fp=fopen($path.$name,"w");
//                    fwrite($fp,$message);
//                    fclose($fp);
//                    $ar=$ar.$name.",";
                    //$data[] = $message;
                    
                }
                // Support for embedded attachments starts here
//                if($struckture->parts[$key]->parts)
//                {
//                    foreach($struckture->parts[$key]->parts as $keyb => $valueb)
//                    {
//                        $enc=$struckture->parts[$key]->parts[$keyb]->encoding;
//                        if($struckture->parts[$key]->parts[$keyb]->ifdparameters)
//                        {
//                            $name=$struckture->parts[$key]->parts[$keyb]->dparameters[0]->value;
//                            $partnro = ($key+1).".".($keyb+1);
//                            $message = imap_fetchbody($this->imap_stream,$mid,$partnro);
//                            if ($enc == 0)
//                                   $message = imap_8bit($message);
//                            if ($enc == 1)
//                                   $message = imap_8bit ($message);
//                            if ($enc == 2)
//                                   $message = imap_binary ($message);
//                            if ($enc == 3)
//                                   $message = imap_base64 ($message);
//                            if ($enc == 4)
//                                   $message = quoted_printable_decode($message);
//                            if ($enc == 5)
//                                   $message = $message;
//                            $fp=fopen($path.$name,"w");
//                            fwrite($fp,$message);
//                            fclose($fp);
//                            $ar=$ar.$name.",";
//                        }
//                    }
//                }
            }
        }
        return $data;
//        $ar=substr($ar,0,(strlen($ar)-1));
//        return $ar;
    }

    /**
     * gets mail body (text or html if available)
     * @param int $mail_number
     * @return string
     */
    function getBody($mail_number)
    {
        if(!$this->imap_stream)
            return false;

        $body = $this->get_part($this->imap_stream, $mail_number, "TEXT/HTML");
        if ($body == "")
            $body = $this->get_part($this->imap_stream, $mail_number, "TEXT/PLAIN");
        if ($body == "") {
            return "";
        }
        $body = $this->decodeMimeString($body);
        return $body;
    }

    /**
     * delete e-mail
     * @param int $mail_number
     * @return boolean
     */
    function deleteMail($mail_number)
    {
        if(!$this->imap_stream) return false;

        return imap_delete($this->imap_stream,$mail_number);
    }

    /**
     * closes connection to the mail server
     * @return boolean Returns TRUE on success or FALSE on failure.
     */
    public function close()
    {
        if(!$this->imap_stream) return true;

        return imap_close($this->imap_stream,CL_EXPUNGE);
    }
    
    /**
     * @author honza
     * 
     * @param int $mail_id mail identificator
     * @return array of attachment parts
     */
    public function getMailAttachments($mail_id) {
        if(!$this->imap_stream)
            return false;
        
        $structure = imap_fetchstructure($this->imap_stream, $mail_id);
        if($structure->type == 0) {
            return;
        }
        $attachments = array();
        $parts = $structure->parts;
        $counter = 0;
        for($i = 0; $i < \count($structure->parts); $i++) {
            if($parts[$i]->ifdisposition === 0)
                continue;
            if($parts[$i]->disposition === "ATTACHMENT") {
                $attachments[] = $this->makeAttachmentEntity($parts[$i], $mail_id, $i+1);
            }
        }
        return $attachments;
    }
    /**
     * @author honza
     * 
     * @param attachment part (i.e. stdClass)
     */
    private function makeAttachmentEntity($part, $mail_id, $part_num, $getData = false) {
        $attEntity = new Attachment();
        $attEntity->setMail_id($mail_id);
        $attEntity->setBytes($part->bytes);
        $attEntity->setType($part->type);
        $attEntity->setPart_id($part_num);
        if($part->subtype)
            $attEntity->setSubtype($part->subtype);
        if($part->parameters)
            $attEntity->setName($this->getParameters($part->parameters, "NAME"));
        if($part->dparameters)
            $attEntity->setFileName($this->getParameters($part->dparameters, "FILENAME"));
        $mime_type = $this->get_mime_type($part);
        $attEntity->setMime_type($mime_type);
        $attEntity->setEncoding($part->encoding);
        if(in_array($mime_type, $this->imageArray))
            $attEntity->setData($this->getAttachData($mail_id, $part_num));
        return $attEntity;
    }
    
    /**
     * @author honza
     * 
     * @param type $parameters array of parameters
     * @param type $pattern what attribute you want to find (e.g.)
     * 
     * @return $value
     */
    private function getParameters($parameters, $pattern) {
        foreach($parameters as $parameter) {
            if($parameter->attribute === $pattern) {
                return $parameter->value;
            }
        }
    }
    
    private function getAttachData($mail_id, $partNum) {
        return imap_fetchbody($this->imap_stream, $mail_id, $partNum);       
    }
    
}